package schema

import (
	"GeoORM/dialect"
	"go/ast"
	"reflect"
)

// Field 每个字段代表表中某一列信息
type Field struct {
	//字段名
	Name string
	//字段类型
	Type string
	//附加信息---主键,非空...
	Tag string
}

// Schema 代表数据库中某张表
type Schema struct {
	//当前表对应的实体对象
	Model interface{}
	//表名
	Name string
	//表的字段信息
	Fields []*Field
	//表字段名
	FieldNames []string
	//表字段名和对应Field实体映射关系
	fieldMap map[string]*Field
}

//GetField 通过字段名获取到对应的Field信息
func (schema *Schema) GetField(name string) *Field {
	return schema.fieldMap[name]
}

//Parse 根据对象实体和当前数据库对应的方言,解析得到当前视图映射到的表对象
func Parse(dest interface{}, d dialect.Dialect) *Schema {
	//先获取到值对象指针,然后Indirect拿到真正的值对象,然后获取值对象的类型
	modelType := reflect.Indirect(reflect.ValueOf(dest)).Type()
	//构造表对象
	schema := &Schema{
		//实体对象
		Model: dest,
		//实体名作为表名
		Name: modelType.Name(),
		//初始化字段map
		fieldMap: make(map[string]*Field),
	}
	//遍历实体对象的字段列表
	for i := 0; i < modelType.NumField(); i++ {
		//拿到属性
		p := modelType.Field(i)
		//当前属性不是匿名自动并且当前属性是被导出的
		if !p.Anonymous && ast.IsExported(p.Name) {
			//构造一个字段对象
			field := &Field{
				//字段名为当前属性名
				Name: p.Name,
				//先获取到当前字段类型反射对象,然后通过方言解析得到对象的数据库表的列类型
				//reflect.New初始化一个当前类型的零值对象,返回一个指向该零值对象的指针
				//Indirect拿到那个零值对象
				Type: d.DataTypeOf(reflect.Indirect(reflect.New(p.Type))),
			}
			//解析当前字段上可能存在的标签
			if v, ok := p.Tag.Lookup("geoOrm"); ok {
				//如果存在geoOrm标签,就保存到当前字段对象中
				field.Tag = v
			}
			//保存当前字段信息
			schema.Fields = append(schema.Fields, field)
			schema.FieldNames = append(schema.FieldNames, p.Name)
			schema.fieldMap[p.Name] = field
		}
	}
	//返回解析好的表对象
	return schema
}

//RecordValues 解析实体对象中的值,变为数据库对应列的值
// Stu{name: dhy ,age: 18} 转换为 insert into stu(name,age) values("dhy",18)中values中实参值
func (schema *Schema) RecordValues(dest interface{}) []interface{} {
	destValue := reflect.Indirect(reflect.ValueOf(dest))
	var fieldValues []interface{}
	for _, field := range schema.Fields {
		fieldValues = append(fieldValues,
			//通过反射去stu对象中获取到name字段对应的value,然后通过Interface()拿到该字段实际的值dhy
			destValue.FieldByName(field.Name).Interface())
	}
	//返回的就是[dhy,18]实际参数数组
	return fieldValues
}
